{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d60f1e23-2541-4b01-84c8-0aff758061b4",
   "metadata": {},
   "source": [
    "# Compiling Unitaries Using Diffusion Models\n",
    "\n",
    "This tutorial is based on the work presented in\n",
    "\n",
    "> [\"Synthesis of discrete-continuous quantum circuits with multimodal diffusion models\", Florian Fürrutter, Zohim Chandani, Ikko Hamamura, Hans J. Briegel & Gorka Muñoz-Gil, arXiv.2506.01666 (2025)](https://doi.org/10.48550/arXiv.2506.01666)\n",
    "\n",
    "and\n",
    "> [\"Quantum circuit synthesis with diffusion models\", Florian Fürrutter, Gorka Muñoz-Gil & Hans J. Briegel , Nat. Mach. Intell. **6**, 515–524 (2024)](https://doi.org/10.1038/s42256-024-00831-9). "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b6a7722-f302-458b-9f90-8493e46d786d",
   "metadata": {},
   "source": [
    "Quantum computing relies on efficiently translating quantum operations into viable physical realizations on existing quantum hardware. Recently, diffusion models — a powerful class of generative models in machine learning — have demonstrated exceptional performance on this task. \n",
    "\n",
    "In this tutorial notebook, we will demonstrate how to use this method to synthesize arbitrary unitaries into a `cudaq.kernel`, effectively decomposing them into sequences of quantum gates, a process commonly known as unitary compilation.\n",
    "\n",
    "Our focus will be on generating circuits using a pre-trained diffusion model. Specifically, we will illustrate how to compile different unitaries into discrete quantum gates, based on the methods presented in [(Fürrutter et al., 2024)](https://doi.org/10.1038/s42256-024-00831-9), and continuous quantum gates, based on [(Fürrutter et al., 2025)](https://doi.org/10.48550/arXiv.2506.01666)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fef3bf4a-1008-4df8-b6e8-d5357db1f6d5",
   "metadata": {},
   "source": [
    "## Diffusion model pipeline\n",
    "\n",
    "Generative diffusion models (DMs) have recently delivered remarkable results across a wide range of applications, from image generation to protein folding. In this work, we leverage DMs to generate quantum circuits based on a user specified unitary matrix and a text prompt that defines the allowed gate set, effectively using DMs as unitary compilers. The method is thoroughly explained in Ref. [(Fürrutter et al., 2024)](https://doi.org/10.1038/s42256-024-00831-9). The following figure  provides an overview of the proposed circuit generation pipeline:\n",
    "\n",
    "<figure>\n",
    "  <img src=\"images/pipeline_simplified.png\" alt=\"Quantum circuit generation pipeline overview\" width=\"70%\"/>\n",
    "  <figcaption><b>Quantum circuit generation pipeline</b>. Figure adapted from <a href=\"https://doi.org/10.1038/s42256-024-00831-9\">(Fürrutter et al., 2024)</a></figcaption>\n",
    "</figure>\n",
    "\n",
    "The pipeline consists of 3 main components:\n",
    "\n",
    "**1) Circuit encoding:** Like any neural network, diffusion models operate with continuous inputs and outputs. However, since the circuits we consider are composed of discrete gates (i.e., with no continuous parameters), we develop a mapping that transforms each gate into a continuous vector. This allows us to represent a given circuit as a three-dimensional tensor, as illustrated. Crucially, this mapping is invertible: when the DM generates continuous tensors, we can apply the inverse map to convert them back into the circuit form. An overview of these steps is provided in the figure below:\n",
    "\n",
    "<figure>\n",
    "  <img src=\"images/circuit_encoding.png\" alt=\"Quantum circuit encoding\" width=\"60%\"/>\n",
    "  <figcaption><b>Quantum circuit encoding</b>. Figure adapted from <a href=\"https://doi.org/10.1038/s42256-024-00831-9\">(Fürrutter et al., 2024)</a></figcaption>\n",
    "</figure>\n",
    "\n",
    "**2) Conditioning:** The user's input (the set of available gates and the unitary to compile) is also transformed into a continuous tensor by two neural networks. For the gate set description, where the input is a text prompt (e.g., \"Compile using ['x', 'h']\"), we utilize a pre-trained language model. For the unitary, we employ a neural network that is trained jointly with the diffusion model.\n",
    "\n",
    "**3) Unitary compilation:** The generation procedure follows the typical DM process: the model is given a fully noisy tensor which is iteratively de-noised until reaching a clean sample based on the given conditioning (the desired unitary and gate set). The tensors generated by the DM are then mapped to circuits via the inverse encoding procedure. To learn more about the practical implementation of diffusion models we recommend [this tutorial](https://course.fast.ai/Lessons/lesson9.html). \n",
    "\n",
    "In the following, we will use `cudaq` and [`genQC`](https://github.com/FlorianFuerrutter/genQC) to perform all these steps and go from a desired unitary matrix $U$ to a quantum circuit that we can execute using CUDA-Q. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d72193b-914a-4bbb-9e30-e0fb0ff3d2d2",
   "metadata": {},
   "source": [
    "> **Extension to discrete-continuous quantum circuits:**\n",
    "Recently, in [Fürrutter et al. (2025)](https://doi.org/10.48550/arXiv.2506.01666), the authors introduced the next generation of compilation models, utilizing not only discrete gates but also continuous quantum gates in a multimodal diffusion model. We refer interested readers to the paper for more advanced details on the method. Moreover, we will also cover its use in the sections below."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1453ad1-e4d4-4e38-9684-fd967bc1a8de",
   "metadata": {},
   "source": [
    "## Setup and load models"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9b15f8a-eda0-4451-a334-5bbc8e11396e",
   "metadata": {},
   "source": [
    "First, we make sure we have a compatible version of `genQC` ([github.com/FlorianFuerrutter/genQC](https://github.com/FlorianFuerrutter/genQC)) installed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d94e9c9c-93d7-4ed4-92a4-968f2439b01e",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install genQC==0.2.3 huggingface-hub==0.36.0 torch --break-system-packages -q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e127f75-a6b0-481a-a48b-35e1363af623",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cudaq\n",
    "import torch\n",
    "import numpy as np\n",
    "import genQC\n",
    "import os\n",
    "\n",
    "os.environ['HF_HUB_DISABLE_PROGRESS_BARS'] = '1'\n",
    "\n",
    "import genQC.utils.misc_utils as util\n",
    "from genQC.pipeline.diffusion_pipeline import DiffusionPipeline\n",
    "from genQC.pipeline.multimodal_diffusion_pipeline \\\n",
    "            import MultimodalDiffusionPipeline_ParametrizedCompilation\n",
    "\n",
    "from genQC.platform.tokenizer.circuits_tokenizer import CircuitTokenizer\n",
    "from genQC.platform.simulation import Simulator, CircuitBackendType\n",
    "from genQC.scheduler.scheduler_dpm import DPMScheduler\n",
    "\n",
    "from genQC.inference.sampling \\\n",
    "            import decode_tensors_to_backend, generate_compilation_tensors\n",
    "from genQC.inference.evaluation_helper import get_unitaries\n",
    "from genQC.inference.eval_metrics import UnitaryInfidelityNorm\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ccb97524-0f13-49ff-893d-983c572e66c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: Cuda device has a capability of 8.6 (>= 8), allowing tf32 matmul.\n",
      "cuda\n"
     ]
    }
   ],
   "source": [
    "device = util.infer_torch_device() # Use CUDA if we have a GPU\n",
    "print(device)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f459fbb6-28b4-4ac5-bd4c-4ef7eb8d9a93",
   "metadata": {},
   "source": [
    "In this tutorial, we will use a small and a large pre-trained diffusion model (up to ~300M parameters). We only run the large model if we have a GPU available, such we have a reasonable computation time of the notebook for CPU-only machines.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f116fa6-11fb-4ad7-a2cb-cfe1ee39d426",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "# Flag to only run large model if GPU available\n",
    "RUN_LARGE_MODEL = ( device == torch.device(\"cuda\") )\n",
    "print(RUN_LARGE_MODEL)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b4838ea-631b-4d6f-b1c3-e73d586c9790",
   "metadata": {},
   "source": [
    "We set a fixed seed for reproducibility."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d40ed123-8527-4fe2-bebd-ce2e312a86cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We set a seed to pytorch, numpy and python. \n",
    "# Note: This will also set deterministic cuda algorithms, possibly at the cost of reduced performance!\n",
    "util.set_seed(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6114952-e8e6-403f-8b79-cbd391ad49af",
   "metadata": {},
   "source": [
    "For evaluation, we also need to specify the `cudaq` circuit simulator backend."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5fb8ec8-e3ec-4249-a368-774f2ab8dbc5",
   "metadata": {},
   "outputs": [],
   "source": [
    "simulator = Simulator(CircuitBackendType.CUDAQ, \n",
    "                      target='qpp-cpu')  # Target for cudaq, note that cpu is faster for low qubit kernels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acf7656c-be92-4b36-8c38-d3ff6c1912e3",
   "metadata": {},
   "source": [
    "### Load discrete model"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "0047cbe9-a21c-4c72-a144-7a2929752329",
   "metadata": {},
   "source": [
    "First, we load pre-trained model weights for a discrete model directly from [Hugging Face: Floki00/qc_unitary_3qubit](https://huggingface.co/Floki00/qc_unitary_3qubit) and setup the DM pipeline. For details of the model we refer to the paper [Fürrutter et al., 2024](https://doi.org/10.1038/s42256-024-00831-9). Note that this model is only trained on 3 qubit unitaries up to 12 gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5c18048-11d8-4717-9836-d484a70440a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "discrete_pipeline = DiffusionPipeline.from_pretrained(\n",
    "            repo_id=\"Floki00/qc_unitary_3qubit\", # Download model from Hugging Face\n",
    "            device=device)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c8ae3a2-af0b-4781-987f-283cd1233cf8",
   "metadata": {},
   "source": [
    "The loaded discrete model is trained with the discrete gate set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e6ddb9e-7357-4514-b669-985125dcce52",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['h', 'cx', 'z', 'x', 'ccx', 'swap']"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "discrete_pipeline.gate_pool"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8bc2d07-b336-490b-938e-46ce357c90f0",
   "metadata": {},
   "source": [
    "which we need in order to define the `discrete_vocabulary` and create a `CircuitTokenizer`, allowing us to decode tokenized circuits generated by the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9403ea3b-01b3-4a95-94fa-2fd602659ce8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'h': 1, 'cx': 2, 'z': 3, 'x': 4, 'ccx': 5, 'swap': 6}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "discrete_vocabulary = {g:i+1 for i, g in enumerate(discrete_pipeline.gate_pool)} \n",
    "discrete_tokenizer  = CircuitTokenizer(discrete_vocabulary)\n",
    "discrete_tokenizer.vocabulary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc112d57-71a8-403c-a0a9-d2f7d0eb7492",
   "metadata": {},
   "source": [
    "Set parameters the model was trained on. Note that these are fixed and depend on the pre-trained model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9010cc92-323d-4bc0-8318-9cb730da1cd0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# These parameters are specific to our pre-trained model.\n",
    "discrete_system_size   = 3\n",
    "discrete_max_gates     = 12"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a774bde-5404-4483-8eb7-a1d3835982ab",
   "metadata": {},
   "source": [
    "**Set inference parameters**\n",
    "\n",
    "Then, we setup diffusion model inference parameters. These can be changed in principle, as they are sample hyperparameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a231960-d9df-4508-80d9-7c7d0903e6eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "timesteps = 40\n",
    "discrete_pipeline.scheduler.set_timesteps(timesteps) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f4fca0b-1583-4c00-8922-0ff2b55648df",
   "metadata": {},
   "source": [
    "### Load continuous model"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "9832e439-97a5-4dfc-8860-86f2b0d1bdd8",
   "metadata": {},
   "source": [
    "Next, we load pre-trained model weights for a discrete-continuous model directly from [Hugging Face: Floki00/cirdit_multimodal_compile_3to5qubit](https://huggingface.co/Floki00/cirdit_multimodal_compile_3to5qubit) and setup the DM pipeline. For details of the model, we refer to the paper [Fürrutter et al., 2025](https://doi.org/10.48550/arXiv.2506.01666). Note that this model is trained on 3 to 5 qubit unitaries up to 32 gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "282b91a6-e3a1-4f3f-8985-715ec52df6bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "cont_pipeline = MultimodalDiffusionPipeline_ParametrizedCompilation.from_pretrained(\n",
    "    repo_id=\"Floki00/cirdit_multimodal_compile_3to5qubit\", # Download model from Hugging Face\n",
    "    device=device)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5c5164e-e065-420c-b924-28e173e523fe",
   "metadata": {},
   "source": [
    "The loaded continuous model is trained with the discrete and continuous gate set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ac6a8e1-0c90-4551-a84c-bc4d7b06a6d9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['h', 'cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cont_pipeline.gate_pool"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42485a83-8f1a-490b-8b02-256983ecd2a1",
   "metadata": {},
   "source": [
    "which we then use in order to define the `cont_vocabulary` and create a `CircuitTokenizer`, allowing us to decode tokenized circuits generated by the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a8dde9f-598e-43f0-ade2-4657e41321dd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'h': 1, 'cx': 2, 'ccx': 3, 'swap': 4, 'rx': 5, 'ry': 6, 'rz': 7, 'cp': 8}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cont_vocabulary = {g:i+1 for i, g in enumerate(cont_pipeline.gate_pool)} \n",
    "cont_tokenizer  = CircuitTokenizer(cont_vocabulary)\n",
    "cont_tokenizer.vocabulary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d23e1bf8-d33f-44a5-ad97-192aa8c79e7a",
   "metadata": {},
   "source": [
    "Now we set parameters the model was trained on. Note that these are fixed and depend on the pre-trained model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1eaa33d8-5722-4246-beda-6551f0c9e9e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# These parameters are specific to our pre-trained model.\n",
    "cont_system_size   = 5\n",
    "cont_max_gates     = 32"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cef7daf5-f85b-46f8-a788-f69262f12e9f",
   "metadata": {},
   "source": [
    "**Set inference parameters**\n",
    "\n",
    "Again, we setup diffusion model inference parameters. These hyperparameters can be changed at will, as they define how sampling is performed from the trained model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0aa71ef-af4b-4597-ac1f-56b7c9ec7f16",
   "metadata": {},
   "outputs": [],
   "source": [
    "cont_pipeline.scheduler   = DPMScheduler.from_scheduler(cont_pipeline.scheduler)\n",
    "cont_pipeline.scheduler_w = DPMScheduler.from_scheduler(cont_pipeline.scheduler_w)\n",
    "\n",
    "timesteps = 40\n",
    "cont_pipeline.scheduler.set_timesteps(timesteps) \n",
    "cont_pipeline.scheduler_w.set_timesteps(timesteps) \n",
    "\n",
    "cont_pipeline.lambda_h = 1.1\n",
    "cont_pipeline.lambda_w = 0.4\n",
    "cont_pipeline.g_h = 0.4\n",
    "cont_pipeline.g_w = 0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a7e2d31-e0a5-4a20-b741-c2de701f2a77",
   "metadata": {},
   "source": [
    "### Create helper functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba41daf5-bcae-4491-bae4-9ef87b9bc5b9",
   "metadata": {},
   "source": [
    "To facilitate the sampling, evaluating and plotting multiple circuits for different unitaries, we create some helper functions here."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec9d4be1-4150-49fd-aa75-9dd12bc59b38",
   "metadata": {},
   "source": [
    "1) A function to check that a matrix `U` is indeed unitary, i.e. $U^{\\dagger} U = U U^{\\dagger} = I$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "623638fb-b697-4654-a64c-2ef219a00f24",
   "metadata": {},
   "outputs": [],
   "source": [
    "def verify_unitary(U: torch.Tensor):\n",
    "    \"\"\"Check if unitary.\"\"\"\n",
    "    assert torch.allclose(U.adjoint() @ U, torch.eye(2**num_of_qubits, dtype=U.dtype))  \n",
    "    assert torch.allclose(U @ U.adjoint(), torch.eye(2**num_of_qubits, dtype=U.dtype))  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e70cd2d-995d-4d1b-b012-29d860419fa3",
   "metadata": {},
   "source": [
    "2) A function to sample the DM and return generated kernels with coresponding infidelities."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7975c631-bfa0-478e-9260-79a7dcf86781",
   "metadata": {},
   "outputs": [],
   "source": [
    "def sample_kernels_and_evaluate(U: torch.Tensor, \n",
    "                                prompt: str, \n",
    "                                num_of_qubits: int, \n",
    "                                samples: int,\n",
    "                                discrete_model: bool,\n",
    "                                return_tensors: bool = False):\n",
    "    \"\"\"\n",
    "    Sample the DM and return generated kernels with coresponding infidelities.\n",
    "    \"\"\"\n",
    "\n",
    "    # 1) Check if unitary\n",
    "    verify_unitary(U)\n",
    "\n",
    "    # 2) Generate tensor representations using the DM based on the prompt and U.\n",
    "    U = U.to(torch.complex64)\n",
    "    \n",
    "    if discrete_model:\n",
    "        # Sample discrete model\n",
    "        out_tensor = generate_compilation_tensors(discrete_pipeline, \n",
    "                                  prompt=prompt, \n",
    "                                  U=U, \n",
    "                                  samples=samples,      # How many circuits we sample per unitary\n",
    "                                  system_size=discrete_system_size, \n",
    "                                  num_of_qubits=num_of_qubits, \n",
    "                                  max_gates=discrete_max_gates,\n",
    "                                  g=10.0,               # classifier-free-guidance (CFG) scale\n",
    "                                  no_bar=False,         # show progress bar\n",
    "                                  auto_batch_size=256,  # for less GPU memory usage limit batch size \n",
    "                                  tensor_prod_pad=False, \n",
    "                                  enable_params=False,\n",
    "                                 )\n",
    "        tokenizer = discrete_tokenizer\n",
    "        params    = None\n",
    "        \n",
    "    else:\n",
    "        if not RUN_LARGE_MODEL:\n",
    "            print(f\">> Skipped sampling large model. Flag: {RUN_LARGE_MODEL=} <<\")\n",
    "            if return_tensors:\n",
    "                return [], [], []\n",
    "            return [], []\n",
    "        \n",
    "        # Sample continuous model\n",
    "        out_tensor, params = generate_compilation_tensors(cont_pipeline, \n",
    "                                  prompt=prompt, \n",
    "                                  U=U, \n",
    "                                  samples=samples,     # How many circuits we sample per unitary\n",
    "                                  system_size=cont_system_size, \n",
    "                                  num_of_qubits=num_of_qubits, \n",
    "                                  max_gates=cont_max_gates,\n",
    "                                  no_bar=False,        # show progress bar\n",
    "                                  auto_batch_size=256, # for less GPU memory usage limit batch size \n",
    "                                 )\n",
    "        tokenizer = cont_tokenizer\n",
    "    \n",
    "    # 3) Convert tensors to kernels \n",
    "    generated_kernels, _, generated_tensors = decode_tensors_to_backend(simulator=simulator, \n",
    "                                                     tokenizer=tokenizer, \n",
    "                                                     tensors=out_tensor, \n",
    "                                                     params=params,\n",
    "                                                     return_tensors=True)\n",
    "\n",
    "    # 4) Evaluate the kernels and return the unitaries\n",
    "    generated_us = get_unitaries(simulator, generated_kernels, num_qubits=num_of_qubits)\n",
    "\n",
    "    # 5) Calculate the infidelities to the target U\n",
    "    infidelities = UnitaryInfidelityNorm.distance(\n",
    "                    approx_U=torch.from_numpy(np.stack(generated_us)).to(torch.complex128), \n",
    "                    target_U=U.unsqueeze(0).to(torch.complex128))\n",
    "\n",
    "    if return_tensors:\n",
    "        return generated_kernels, infidelities, generated_tensors\n",
    "    return generated_kernels, infidelities"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9447d240-ba4f-4372-b277-6c3e71c7a525",
   "metadata": {},
   "source": [
    "3) A function to plot the `topk` best generated kernels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfc9eea3-8eda-4192-9020-b928abf892cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_topk_kernels(generated_kernels: list, \n",
    "                      infidelities: torch.Tensor, \n",
    "                      num_of_qubits:int, \n",
    "                      topk: int):\n",
    "    \"\"\"\n",
    "    Plot the topk best generated kernels.\n",
    "    \"\"\"\n",
    "\n",
    "    # Get topk indices\n",
    "    best_indices = np.argsort(infidelities)[:topk]\n",
    "\n",
    "    input_state = [0] * (2**num_of_qubits)\n",
    "    input_state[0] = 1\n",
    "\n",
    "    # Print the circuits\n",
    "    for i, best_index in enumerate(best_indices):\n",
    "        kernel = generated_kernels[best_index].kernel\n",
    "        thetas = generated_kernels[best_index].params\n",
    "        \n",
    "        print(f\"Circuit has an infidelity of {infidelities[best_index].item():0.1e}.\")\n",
    "        print(cudaq.draw(kernel, input_state, thetas))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4349fc84-171c-4c75-a47b-fb3a93b718b2",
   "metadata": {},
   "source": [
    "## Unitary compilation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9477f99-0679-4bb6-8b98-2735264fb169",
   "metadata": {},
   "source": [
    "We start by defining the unitaries we want to compile and then sample the corresponding DM. Note that these models have been trained to compile unitaries that arise from circuits composed of the gates contained in their `vocabulary`. While these are universal gate sets, i.e. they can perform universal computation, they can only do so with an arbitrary-precision for an infinite number of gates. Because the number of gates in the models are restricted to some `max_gates` (12 for the discrete model and 32 for the continuous one), we can only expect the models to generate unitaries under this constraint. We will consider here the compilation of such unitaries. Nonetheless, stay tuned for bigger and better models!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "624792ca-b216-4251-8223-d762462cdf48",
   "metadata": {},
   "source": [
    "### Random unitary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1f6c80d-6c4a-4858-a524-44bf10430c26",
   "metadata": {},
   "source": [
    "Let's start with a random 3-qubit unitary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2348c3c-fa3f-47f6-8e09-38a0e25e86c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_of_qubits = 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f609d73-abe6-405f-8960-a342033e223b",
   "metadata": {},
   "source": [
    "We can define our arbitrary unitary `U` directly as a complex `torch.tensor`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69e01056-21c5-42de-8446-9fa635d4d17c",
   "metadata": {},
   "outputs": [],
   "source": [
    "U = torch.tensor([[0.70710678, 0., 0., 0., 0.70710678, 0., 0., 0.],\n",
    "               [0., -0.70710678, 0., 0., 0., -0.70710678, 0., 0.],\n",
    "               [-0.70710678, 0., 0., 0., 0.70710678, 0., 0., 0.],\n",
    "               [0., 0.70710678, 0., 0., 0., -0.70710678, 0., 0.],\n",
    "               [0., 0., 0.70710678, 0., 0., 0., 0., 0.70710678],\n",
    "               [0., 0., 0., 0.70710678, 0., 0., 0.70710678, 0.],\n",
    "               [0., 0., -0.70710678, 0., 0., 0., 0., 0.70710678],\n",
    "               [0., 0., 0., -0.70710678, 0., 0., 0.70710678, 0.]],              \n",
    "              dtype=torch.complex128)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ae15acc-b2cc-4cae-bf91-817fada56676",
   "metadata": {},
   "source": [
    "### Discrete model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7aa2e9f-5d51-42ee-afc1-533254018674",
   "metadata": {},
   "source": [
    "The loaded discrete model was trained on the gate set `['h', 'cx', 'z', 'x', 'ccx', 'swap']`. Specifically, it was trained to generate circuits using any arbitrary subset of this gate set. Therefore, during inference, we can instruct the model to compile the unitary using any of these subsets. However, it is crucial to follow the prompt structure `Compile using [...]`, as the model was trained with this specific format. \n",
    "\n",
    "For example, let's consider a scenario where we compile the unitary without using the `x` gate:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbd80460-671d-4a84-b3d5-dcf28febaf36",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Notice how the x gate is missing from the prompt since this is a restriction we set\n",
    "prompt = \"Compile using: ['h', 'cx', 'z', 'ccx', 'swap']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa556ec0-65fe-4c6f-95ec-42b3ee029c96",
   "metadata": {},
   "source": [
    "Now, we call the diffusion model pipeline to generate encoded circuits based on the specified conditions: `prompt` and `U`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "100726ab-7ad0-40b8-b525-077fbbb90c6b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e166fc7046134c10973797efc69c6e8c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 128 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities = sample_kernels_and_evaluate(\n",
    "                                          U=U, \n",
    "                                          prompt=prompt, \n",
    "                                          num_of_qubits=num_of_qubits, \n",
    "                                          samples=128,\n",
    "                                          discrete_model=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc6e24ed-1196-46a8-8566-447c5c23e117",
   "metadata": {},
   "source": [
    "Next, we plot the best three circuits in the following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "342fdf03-da4f-49ed-8513-0db43e3c7b26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit has an infidelity of 3.4e-08.\n",
      "     ╭───╮╭───╮        ╭───╮╭───╮     \n",
      "q0 : ┤ x ├┤ z ├─────●──┤ z ├┤ z ├──●──\n",
      "     ╰─┬─╯╰───╯   ╭─┴─╮├───┤╰───╯╭─┴─╮\n",
      "q1 : ──●────────╳─┤ x ├┤ z ├─────┤ x ├\n",
      "       │  ╭───╮ │ ╰─┬─╯╰───╯     ╰─┬─╯\n",
      "q2 : ──●──┤ h ├─╳───●──────────────●──\n",
      "          ╰───╯                       \n",
      "\n",
      "Circuit has an infidelity of 3.4e-08.\n",
      "     ╭───╮╭───╮                  \n",
      "q0 : ┤ x ├┤ z ├─────●─────────●──\n",
      "     ╰─┬─╯╰───╯   ╭─┴─╮╭───╮╭─┴─╮\n",
      "q1 : ──●────────╳─┤ x ├┤ z ├┤ x ├\n",
      "       │  ╭───╮ │ ╰─┬─╯╰───╯╰─┬─╯\n",
      "q2 : ──●──┤ h ├─╳───●─────────●──\n",
      "          ╰───╯                  \n",
      "\n",
      "Circuit has an infidelity of 3.4e-08.\n",
      "     ╭───╮╭───╮                  \n",
      "q0 : ┤ x ├┤ z ├─────●─────────●──\n",
      "     ╰─┬─╯╰───╯   ╭─┴─╮╭───╮╭─┴─╮\n",
      "q1 : ──●────────╳─┤ x ├┤ z ├┤ x ├\n",
      "       │  ╭───╮ │ ╰─┬─╯╰───╯╰─┬─╯\n",
      "q2 : ──●──┤ h ├─╳───●─────────●──\n",
      "          ╰───╯                  \n",
      "\n"
     ]
    }
   ],
   "source": [
    "plot_topk_kernels(generated_kernels, infidelities, num_of_qubits, topk=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4de1c88e-4111-4a15-bd88-e93b376b42d8",
   "metadata": {},
   "source": [
    "### Continuous model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55b629e2-a133-4fbb-91b0-256220a387c1",
   "metadata": {},
   "source": [
    "Now we want to compile the same `U` with the continuous model.\n",
    "\n",
    "The continuous model was trained on the gate set `['h', 'cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']`. As the discrete model, it was trained to generate circuits using any arbitrary subset of this gate set. However, it is crucial to follow the prompt structure `Compile {num_of_qubits} qubits using: [...]`, as the model was trained with this specific format. \n",
    "\n",
    "For example, let's consider a scenario where we compile the unitary without using the `h` gate. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6695e2a4-22fc-4f5f-9877-f22e12861e98",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Notice how the h gate is missing from the prompt since this is a restriction we set\n",
    "prompt = f\"Compile {num_of_qubits} qubits using: ['cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9e6bc60-23c2-4cdf-984c-6cd4d84d1e3b",
   "metadata": {},
   "source": [
    "Now, we call the diffusion model pipeline to generate encoded circuits based on the specified conditions: `prompt` and `U`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a952c57-9d6d-406d-ac99-c2c1c4b201ca",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5c1947052acd447ca1ccc2b01c98a2c3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 64 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities = sample_kernels_and_evaluate(\n",
    "                                          U=U, \n",
    "                                          prompt=prompt, \n",
    "                                          num_of_qubits=num_of_qubits, \n",
    "                                          samples=64,\n",
    "                                          discrete_model=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15d6d164-8ef4-4955-8201-9a80b42f14f2",
   "metadata": {},
   "source": [
    "Next, we plot the best three circuits in the following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0e6c1a2-5c4c-4f30-b7d2-0423a5d104b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit has an infidelity of 2.6e-05.\n",
      "     ╭───╮                                        \n",
      "q0 : ┤ x ├────●─────────────●────────●────●────●──\n",
      "     ╰─┬─╯  ╭─┴─╮           │      ╭─┴─╮  │  ╭─┴─╮\n",
      "q1 : ──●────┤ x ├────╳──────┼──────┤ x ├──┼──┤ x ├\n",
      "       │  ╭─┴───┴──╮ │ ╭────┴─────╮╰───╯╭─┴─╮╰───╯\n",
      "q2 : ──●──┤ ry(11) ├─╳─┤ r1(3.15) ├─────┤ x ├─────\n",
      "          ╰────────╯   ╰──────────╯     ╰───╯     \n",
      "\n",
      "Circuit has an infidelity of 3.1e-05.\n",
      "     ╭───╮                                                    \n",
      "q0 : ┤ x ├──────────────────────●────────●────────●────────●──\n",
      "     ╰─┬─╯   ╭───────────╮      │        │        │        │  \n",
      "q1 : ──●───╳─┤ ry(11.01) ├──────┼────────┼────────┼────────┼──\n",
      "       │   │ ╰───────────╯╭─────┴─────╮╭─┴─╮╭─────┴─────╮╭─┴─╮\n",
      "q2 : ──●───╳──────────────┤ r1(6.279) ├┤ x ├┤ r1(3.139) ├┤ x ├\n",
      "                          ╰───────────╯╰───╯╰───────────╯╰───╯\n",
      "\n",
      "Circuit has an infidelity of 3.5e-05.\n",
      "     ╭───╮                                    \n",
      "q0 : ┤ x ├─────●──────────────────●────────●──\n",
      "     ╰─┬─╯     │  ╭────────╮      │        │  \n",
      "q1 : ──●───╳───┼──┤ ry(11) ├──────┼────────┼──\n",
      "       │   │ ╭─┴─╮╰────────╯╭─────┴─────╮╭─┴─╮\n",
      "q2 : ──●───╳─┤ x ├──────────┤ r1(3.131) ├┤ x ├\n",
      "             ╰───╯          ╰───────────╯╰───╯\n",
      "\n"
     ]
    }
   ],
   "source": [
    "plot_topk_kernels(generated_kernels, infidelities, num_of_qubits, topk=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ffab6f6-f9a5-472b-aca4-88cf36b002a7",
   "metadata": {},
   "source": [
    "Now consider a scenario where we compile the unitary without using the `swap` gate and see what changes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14eab750-7189-4e22-9db3-cc42654fd525",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Notice how the swap gate is missing from the prompt since this is a restriction we set\n",
    "prompt = f\"Compile {num_of_qubits} qubits using: ['h', 'cx', 'ccx', 'rx', 'ry', 'rz', 'cp']\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f1135ee-37f7-4308-b025-5499a9ca0da6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "7def075a0e3b479682d8ae357cb0e9d5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 64 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities = sample_kernels_and_evaluate(\n",
    "                                          U=U, \n",
    "                                          prompt=prompt, \n",
    "                                          num_of_qubits=num_of_qubits, \n",
    "                                          samples=64,\n",
    "                                          discrete_model=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aca01d1e-e7b4-4eee-b108-887eae107a5b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit has an infidelity of 1.2e-04.\n",
      "     ╭───╮                                              \n",
      "q0 : ┤ x ├──────●───────────────────────────●────────●──\n",
      "     ╰─┬─╯    ╭─┴─╮         ╭───╮           │        │  \n",
      "q1 : ──●──────┤ x ├──────●──┤ x ├──●────────┼────────┼──\n",
      "       │  ╭───┴───┴───╮╭─┴─╮╰─┬─╯╭─┴─╮╭─────┴─────╮╭─┴─╮\n",
      "q2 : ──●──┤ ry(10.99) ├┤ x ├──●──┤ x ├┤ r1(3.167) ├┤ x ├\n",
      "          ╰───────────╯╰───╯     ╰───╯╰───────────╯╰───╯\n",
      "\n",
      "Circuit has an infidelity of 1.9e-04.\n",
      "     ╭───╮    ╭───╮         ╭───╮╭───────────╮     \n",
      "q0 : ┤ x ├────┤ h ├─────────┤ x ├┤ ry(10.97) ├─────\n",
      "     ╰─┬─╯    ╰───╯         ╰─┬─╯╰───┬───┬───╯     \n",
      "q1 : ──●─────────────────●────●──────┤ x ├──────●──\n",
      "       │  ╭───────────╮╭─┴─╮         ╰─┬─╯    ╭─┴─╮\n",
      "q2 : ──●──┤ ry(10.99) ├┤ x ├───────────●──────┤ x ├\n",
      "          ╰───────────╯╰───╯                  ╰───╯\n",
      "\n",
      "Circuit has an infidelity of 2.7e-04.\n",
      "     ╭───╮  ╭───╮                  ╭───╮╭───────────╮\n",
      "q0 : ┤ x ├──┤ h ├──────────────────┤ x ├┤ ry(10.96) ├\n",
      "     ╰─┬─╯  ╰───╯   ╭───╮     ╭───╮╰─┬─╯╰───────────╯\n",
      "q1 : ──●────────────┤ x ├──●──┤ x ├──┼───────────────\n",
      "       │  ╭────────╮╰─┬─╯╭─┴─╮╰─┬─╯  │               \n",
      "q2 : ──●──┤ ry(11) ├──●──┤ x ├──●────●───────────────\n",
      "          ╰────────╯     ╰───╯                       \n",
      "\n"
     ]
    }
   ],
   "source": [
    "plot_topk_kernels(generated_kernels, infidelities, num_of_qubits, topk=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2acb62cc-a040-4f8b-a8ce-d72b78a79a82",
   "metadata": {},
   "source": [
    "Interestingly, we can see that the model correctly uses the replacement of a `swap` operation with 3 `cx` gates!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89e714e0-41ee-455f-b795-83381b5eeef7",
   "metadata": {},
   "source": [
    "### Quantum Fourier transform"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e75bc7e-468c-4923-9eab-f6702a979aac",
   "metadata": {},
   "source": [
    "We now turn ourselves to more interesting unitary. For instance, let's compile the 4-qubit Quantum Fourier transform (QFT) unitary, defined as\n",
    "$$\n",
    "\\begin{equation}\n",
    "   \\mathrm{QFT}: |x\\rangle \\mapsto \\frac{1}{\\sqrt{N}} \\sum_{k=0}^{N-1}  \\omega_N^{xk}\\;|k\\rangle,\n",
    "\\end{equation}\n",
    "$$\n",
    "where\n",
    "$$\n",
    "\\begin{equation}\n",
    "    \\omega_N=\\exp{\\frac{2\\pi i}{N}} \\quad\\text{and}\\quad N=2^{\\text{qubits}}.\n",
    "\\end{equation}\n",
    "$$\n",
    "For this task, we use the continuous model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9794b6c-b0e7-43db-90e4-eeab7639c633",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_of_qubits = 4\n",
    "prompt        = f\"Compile {num_of_qubits} qubits using: ['h', 'cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42a200c1-c5fb-428c-8c32-5100d3dc8cf8",
   "metadata": {},
   "source": [
    "We can get the QFT unitary from `genqc`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1375cc08-defc-43e2-8474-6c74a63ef2cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from genQC.benchmark.bench_compilation import SpecialUnitaries\n",
    "U = SpecialUnitaries.QFT(num_of_qubits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be2c0bfa-2736-4115-b4b8-5e9aeff5f1ac",
   "metadata": {},
   "source": [
    "As before, we sample and plot the best circuits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f5d1f55-dbec-4095-957e-1869fb850ac2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e3b8adfe60cf44f0bb21529bb6c957ff",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 64 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities = sample_kernels_and_evaluate(\n",
    "                                          U=U, \n",
    "                                          prompt=prompt, \n",
    "                                          num_of_qubits=num_of_qubits, \n",
    "                                          samples=64,\n",
    "                                          discrete_model=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46178b34-e357-4663-9c81-9655afc8bd3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit has an infidelity of 1.6e-02.\n",
      "        ╭───╮                                                            »\n",
      "q0 : ─╳─┤ h ├──────●────────────●────────────●───────────────────────────»\n",
      "      │ ╰───╯      │            │            │                           »\n",
      "q1 : ─┼────────────┼────────────┼────────────┼─────────────────●───────╳─»\n",
      "      │            │      ╭─────┴─────╮      │      ╭───╮╭─────┴─────╮ │ »\n",
      "q2 : ─┼────────────┼──────┤ r1(1.617) ├──────┼──────┤ h ├┤ r1(1.687) ├─╳─»\n",
      "      │      ╭─────┴─────╮╰───────────╯╭─────┴─────╮╰───╯╰───────────╯   »\n",
      "q3 : ─╳──────┤ r1(7.905) ├─────────────┤ r1(11.37) ├─────────────────────»\n",
      "             ╰───────────╯             ╰───────────╯                     »\n",
      "\n",
      "################################################################################\n",
      "\n",
      "                                                   \n",
      "──────●────────────────────────────────────────────\n",
      "      │                                            \n",
      "──────┼──────────────────●─────────────────────────\n",
      "╭─────┴──────╮╭───╮      │                         \n",
      "┤ r1(0.8472) ├┤ h ├──────┼─────────────●───────────\n",
      "╰────────────╯╰───╯╭─────┴──────╮╭─────┴─────╮╭───╮\n",
      "───────────────────┤ r1(0.9612) ├┤ r1(7.672) ├┤ h ├\n",
      "                   ╰────────────╯╰───────────╯╰───╯\n",
      "\n",
      "Circuit has an infidelity of 4.5e-02.\n",
      "                                                                          »\n",
      "q0 : ────────────────────────╳───────●────────────────────────────────────»\n",
      "                             │ ╭─────┴──────╮                             »\n",
      "q1 : ────────────────────────┼─┤ r1(0.8751) ├──────●────────────────────╳─»\n",
      "                       ╭───╮ │ ╰────────────╯╭─────┴─────╮              │ »\n",
      "q2 : ───────────●──────┤ h ├─┼───────────────┤ r1(7.991) ├──────●───────╳─»\n",
      "     ╭───╮╭─────┴─────╮╰───╯ │               ╰───────────╯╭─────┴─────╮   »\n",
      "q3 : ┤ h ├┤ r1(1.796) ├──────╳────────────────────────────┤ r1(7.399) ├───»\n",
      "     ╰───╯╰───────────╯                                   ╰───────────╯   »\n",
      "\n",
      "################################################################################\n",
      "\n",
      "                       \n",
      "───────────────────────\n",
      "                       \n",
      "───────────────────────\n",
      "╭───╮                  \n",
      "┤ h ├──────●───────────\n",
      "╰───╯╭─────┴─────╮╭───╮\n",
      "─────┤ r1(1.437) ├┤ h ├\n",
      "     ╰───────────╯╰───╯\n",
      "\n"
     ]
    }
   ],
   "source": [
    "plot_topk_kernels(generated_kernels, infidelities, num_of_qubits, topk=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "217cb55e-2e0e-4588-8ea5-9efff6c8e754",
   "metadata": {},
   "source": [
    "### XXZ-Hamiltonian evolution"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f6eda48-b0aa-49c6-ac1a-8ae0de9d35a5",
   "metadata": {},
   "source": [
    "Another interesting application is the compilation of the unitary evolution of given Hamiltonians. Here, we showcase this by compiling the evolution unitary $U(\\tau)=\\exp(-i \\tau H)$ of the XXZ-Hamiltonian, defined as\n",
    "$$\n",
    "H_\\text{xxz} = -J \\sum_{\\langle i, j \\rangle} ( X_i X_j + Y_i Y_j + \\Delta Z_i Z_j ) - h \\sum_{i=0}^{n-1} X_i,\n",
    "$$\n",
    "where $J$ is the coupling constant, $\\Delta$ a perturbation and $h$ a magnetic field. We consider here the case in which the $n$ qubits of the quantum circuit represent the spins of a non-periodic one-dimensional chain, where we write neighboring spins $i$ and $j$ as ${\\langle i, j \\rangle}$. Further, we define this Hamiltonian in terms of the Pauli operators, which are defined as\n",
    "$$\n",
    "X = \\begin{pmatrix}0 & 1 \\\\ 1 & 0\\end{pmatrix},\\;\n",
    "Y = \\begin{pmatrix}0 & -i \\\\ i & 0\\end{pmatrix}\\;\\text{and}\\;\n",
    "Z = \\begin{pmatrix}1 & 0 \\\\ 0 & -1\\end{pmatrix}.\n",
    "$$\n",
    "For this task, we use the continuous model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c5463b9-845b-4bee-94d4-ba27ef775d8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_of_qubits = 3\n",
    "prompt        = f\"Compile {num_of_qubits} qubits using: ['h', 'cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8b21fbe-edf0-4e57-84c4-6c38180aae94",
   "metadata": {},
   "source": [
    "We can get the XXZ-Hamiltonian evolution unitary from `genqc`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4132d26f-bb50-4fcf-9c7f-c994ad9646f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from genQC.benchmark.bench_compilation import XXZHamiltonian\n",
    "\n",
    "hamiltonian = XXZHamiltonian(h=0.2, \n",
    "                             J=0.4, \n",
    "                             delta=0.4, \n",
    "                             num_qubits=num_of_qubits, \n",
    "                             periodic_boundary=False)\n",
    "\n",
    "U = hamiltonian.get_evolution(t=0.25)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec7ac93a-6bb5-4154-a2c4-4ccbe4ddf504",
   "metadata": {},
   "source": [
    "As before, we sample and plot the best circuits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3503cff6-d51d-4839-b17f-7d96d3f40655",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d1add04030574e679ff8cb305d1231ed",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 64 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities = sample_kernels_and_evaluate(\n",
    "                                          U=U, \n",
    "                                          prompt=prompt, \n",
    "                                          num_of_qubits=num_of_qubits, \n",
    "                                          samples=64,\n",
    "                                          discrete_model=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17ad3475-5c6b-4bf1-97d7-3c87690ed758",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit has an infidelity of 3.5e-02.\n",
      "     ╭───────────╮                                            \n",
      "q0 : ┤ rx(12.43) ├────────────────────────────────────────────\n",
      "     ╰───┬───┬───╯                          ╭───╮             \n",
      "q1 : ────┤ x ├──────────●───────────────────┤ x ├─────────────\n",
      "         ╰─┬─╯    ╭─────┴─────╮╭───────────╮╰─┬─╯╭───────────╮\n",
      "q2 : ──────●──────┤ r1(12.51) ├┤ rx(12.38) ├──●──┤ rx(12.47) ├\n",
      "                  ╰───────────╯╰───────────╯     ╰───────────╯\n",
      "\n",
      "Circuit has an infidelity of 3.6e-02.\n",
      "     ╭───────────╮                            \n",
      "q0 : ┤ rx(12.42) ├────────────────────────────\n",
      "     ╰───┬───┬───╯                       ╭───╮\n",
      "q1 : ────┤ x ├───────────────●───────────┤ x ├\n",
      "         ╰─┬─╯    ╭───╮╭─────┴─────╮╭───╮╰─┬─╯\n",
      "q2 : ──────●──────┤ h ├┤ r1(12.44) ├┤ h ├──●──\n",
      "                  ╰───╯╰───────────╯╰───╯     \n",
      "\n",
      "Circuit has an infidelity of 3.7e-02.\n",
      "     ╭───────────╮                               \n",
      "q0 : ┤ rx(6.256) ├───────────────────────────────\n",
      "     ╰───────────╯╭───────────╮                  \n",
      "q1 : ──────●──────┤ rx(12.34) ├───────────────●──\n",
      "         ╭─┴─╮    ├───────────┤╭───────────╮╭─┴─╮\n",
      "q2 : ────┤ x ├────┤ rx(6.245) ├┤ rz(12.44) ├┤ x ├\n",
      "         ╰───╯    ╰───────────╯╰───────────╯╰───╯\n",
      "\n"
     ]
    }
   ],
   "source": [
    "plot_topk_kernels(generated_kernels, infidelities, num_of_qubits, topk=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87e7c5db-d9c7-4659-a1f9-eb499f7be21c",
   "metadata": {},
   "source": [
    "## Choosing the circuit you need"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2f84845-9820-4d8b-a3db-396bafa0b379",
   "metadata": {},
   "source": [
    "As mentioned earlier, one of the key advantages of using diffusion models (DMs) as a unitary compiler is the ability to rapidly sample many circuits. However, as is common in machine learning, the model has a certain accuracy, meaning not all generated circuits are expected to exactly compile the specified unitary. In this section, we will evaluate how many of the generated circuits are indeed correct and then perform post-selection to identify circuits that successfully perform the desired unitary operation."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66c3b49d-26cb-4d7f-afc2-4d668f1e99c7",
   "metadata": {},
   "source": [
    "Let's revisit the random unitary from before, but this time we closer inspect what the model generates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9f9c5c36-0412-4067-969a-91d7bfa0c0c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_of_qubits = 3\n",
    "prompt = f\"Compile {num_of_qubits} qubits using: ['h', 'cx', 'ccx', 'swap', 'rx', 'ry', 'rz', 'cp']\"\n",
    "\n",
    "U = torch.tensor([[0.70710678, 0., 0., 0., 0.70710678, 0., 0., 0.],\n",
    "               [0., -0.70710678, 0., 0., 0., -0.70710678, 0., 0.],\n",
    "               [-0.70710678, 0., 0., 0., 0.70710678, 0., 0., 0.],\n",
    "               [0., 0.70710678, 0., 0., 0., -0.70710678, 0., 0.],\n",
    "               [0., 0., 0.70710678, 0., 0., 0., 0., 0.70710678],\n",
    "               [0., 0., 0., 0.70710678, 0., 0., 0.70710678, 0.],\n",
    "               [0., 0., -0.70710678, 0., 0., 0., 0., 0.70710678],\n",
    "               [0., 0., 0., -0.70710678, 0., 0., 0.70710678, 0.]],              \n",
    "              dtype=torch.complex128)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e11aae7a-fc37-4894-aaf2-a996f2691b3b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b83adb71244c41138c454fdaa40d52e4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/40 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]: (generate_comp_tensors) Generated 128 tensors\n"
     ]
    }
   ],
   "source": [
    "generated_kernels, infidelities, generated_tensors = \\\n",
    "                sample_kernels_and_evaluate(\n",
    "                              U=U, \n",
    "                              prompt=prompt, \n",
    "                              num_of_qubits=num_of_qubits, \n",
    "                              samples=128,\n",
    "                              discrete_model=not RUN_LARGE_MODEL,\n",
    "                              return_tensors=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44a7e9fb-1174-4a09-a3d3-94410c1b8dce",
   "metadata": {},
   "source": [
    "First, we plot a histogram of the infidelities of our generated circuits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85f41978-f3da-4869-bd91-84b9b67c3427",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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7/PPPxcODg7CyspKVK5cWXz//fe5tpdN38/r8ePHRcuWLUWZMmWElZWV8PDwEL169dLqGBgL9pgZse7du+P777/Hzp070bdvX411Ll68iLZt26JWrVqYMGECLC0tcePGDRw6dAgAUK1aNUyYMAHjxo1Dv3790KRJEwBA48aNVW08ffoUrVq1QpcuXfDll1/Cyckpz7gmT54MhUKBUaNG4dGjR5g1axaCg4Nx5swZVc+eNrSJ7W1CCHz88cfYu3cvvv76a9SpUwc7duzAiBEjcP/+fcycOVOt/sGDB7FhwwYMGDAANjY2mDNnDjp27Ii7d++iTJkyucb16tUrBAUF4caNGxg0aBA8PT2xbt069OzZE4mJiRgyZAiqVauG5cuXY9iwYShfvjz+85//AADKli2r9f5nGzx4MOzt7REWFoa4uDjMmjULgwYNUo0jWr58OX777TccO3YMv//+e57HKD4+Hk2bNkVGRgZGjx6NkiVL4rffftN4XpYvX44ePXogJCQE06ZNQ2pqKubPnw9/f3+cPn1a64HKAQEB+O677zBnzhx8//33qFatGgCo/vuurKwsfPzxxzh48CD69euHatWq4fz585g5cyauXbuGTZs2Acj/2takbNmyWL58OSZPnowXL14gIiJCFYuu18+ePXuwdu1aDBo0CA4ODgU6cDs+Ph4lSpRAiRIlVGW7du1C1apVER0drYrR3t4eAwcOxPjx42FikvcQ5NDQUEyfPh3t2rVDSEgIzp49i5CQELx+/VqtXmpqKgIDA3H//n188803qFChAv755x+Ehobi4cOHmDVrllr9lStXIiUlBd988w0UCgWmT5+OTz/9FLdu3VL1Cl28eBF+fn4oV66c6jpcu3YtOnTogD///BOffPKJWpsDBgxA2bJlMW7cOLx8+RIAcPz4cfzzzz/o0qULypcvj7i4OMyfPx9BQUG4dOkSSpQoke+1p+01vnPnTnTs2BHVq1dHREQEnj59il69eqF8+fJanb+oqCj07t0bNWrUQGhoKOzs7HD69GnExMTgiy++yHPdyMhIvH79Gv369YOlpSVKly6Nc+fOoUmTJjA3N0e/fv3g4eGBmzdvYsuWLZg8ebJWMeUmr2P26NEjtGzZEmXLlsXo0aNhZ2eHuLg4bNiwQa9tFjtFnRlSwcmvx0wIIWxtbcWHH36oev/uX0MzZ84U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",
      "text/plain": [
       "<Figure size 700x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.figure(figsize=(7, 4))\n",
    "plt.title(\n",
    "    f\"Distribution of infidelities for {len(infidelities)} generated circuits\",\n",
    "    fontsize=12)\n",
    "plt.ylabel(\"Number of circuits\", fontsize=14)\n",
    "plt.xlabel(\"Unitary infidelity\", fontsize=14)\n",
    "plt.hist(infidelities, bins=30)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cc0a87c-58ca-4b4b-92ad-db91067d9d22",
   "metadata": {},
   "source": [
    "As we see above, we now have around 15 kernels that compile the desired unitary! This is particularly valuable when dealing with hardware constraints, where, for instance, we might want to avoid using certain qubits or specific gates. One practical example is finding the circuit with the fewest CNOT gates (also known as `cx`). In our `cont_vocabulary` definition above, we identified that `cx` corresponds to the label 2 in our tokenized tensors. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa25cd6f-076b-42c9-a2f7-43174078bac7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cx_token = cont_vocabulary[\"cx\"]\n",
    "cx_token"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8df96faf-23cd-46f9-b475-7a96bc308ebd",
   "metadata": {},
   "source": [
    "Let's use this information to search for the circuit that minimizes the number of `cx` gates:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "601dfa38-1427-4ee7-b23c-99b322d550a2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The model generated 14 distinct circuits with infidelity < 0.01.\n",
      "These circuits have this number of cx gates: [0, 2, 1, 0, 2, 2, 3, 2, 2, 1, 2, 1, 1, 1]\n"
     ]
    }
   ],
   "source": [
    "# First, we remove possible duplicates and only pick distinct circuits\n",
    "_, idx_unique = torch.unique(generated_tensors, dim=0, return_inverse=True)\n",
    "unique_tensors = generated_tensors[idx_unique]\n",
    "unique_infidelities = infidelities[idx_unique]\n",
    "unique_kernels = [generated_kernels[idx] for idx in idx_unique]\n",
    "\n",
    "# Then, find the correct circuits\n",
    "idx_correct = torch.argwhere(unique_infidelities < 0.01).flatten()\n",
    "correct_tensors = unique_tensors[idx_correct]\n",
    "print(\n",
    "    f\"The model generated {correct_tensors.shape[0]} distinct circuits with infidelity < 0.01.\"\n",
    ")\n",
    "\n",
    "# Now let's flatten the last two dimensions (related to the actual circuit) and find out how many 2's (i.e. cx) gates each circuit has:\n",
    "num_cx = (correct_tensors.flatten(1, 2) == cx_token).sum(1)\n",
    "print(\"These circuits have this number of cx gates:\", num_cx.tolist())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1ac7b92-d1e5-4e8d-a61f-b0ce85db4b08",
   "metadata": {},
   "source": [
    "As we can see, the diffusion model (DM) uses 0 to 3 CNOT gates to compile the unitary. We can now print a few of these circuits to select the one that best suits our needs, or to study whether there are any interesting patterns the model employs for this specific unitary.\n",
    "\n",
    "For instance, we can sort the circuits by their `cx` count and plot them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "909961ef-3a98-4d6e-8ba1-eb42e0652c05",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated circuit with 0 cx:\n",
      "     ╭───╮                     ╭───────────╮\n",
      "q0 : ┤ x ├──────────────●──────┤ rz(3.132) ├\n",
      "     ╰─┬─╯              │      ├───────────┤\n",
      "q1 : ──●────────╳───────┼──────┤ rz(9.379) ├\n",
      "       │  ╭───╮ │ ╭─────┴─────╮╰───────────╯\n",
      "q2 : ──●──┤ h ├─╳─┤ r1(3.084) ├─────────────\n",
      "          ╰───╯   ╰───────────╯             \n",
      "\n",
      "Generated circuit with 0 cx:\n",
      "     ╭───╮╭───────────╮╭───╮                \n",
      "q0 : ┤ x ├┤ ry(1.567) ├┤ h ├──────●─────────\n",
      "     ╰─┬─╯╰───────────╯╰───╯╭─────┴─────╮   \n",
      "q1 : ──●────────────────────┤ r1(3.127) ├─╳─\n",
      "       │   ╭────────╮       ╰───────────╯ │ \n",
      "q2 : ──●───┤ ry(11) ├─────────────────────╳─\n",
      "           ╰────────╯                       \n",
      "\n",
      "Generated circuit with 1 cx:\n",
      "     ╭───╮    ╭───╮       ╭───╮╭───────────╮\n",
      "q0 : ┤ x ├────┤ h ├───────┤ x ├┤ ry(4.683) ├\n",
      "     ╰─┬─╯    ╰───╯       ╰─┬─╯╰───────────╯\n",
      "q1 : ──●────────────────╳───┼───────────────\n",
      "       │  ╭───────────╮ │   │               \n",
      "q2 : ──●──┤ ry(4.722) ├─╳───●───────────────\n",
      "          ╰───────────╯                     \n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Get the correct kernels\n",
    "correct_kernels = [unique_kernels[idx] for idx in idx_correct]\n",
    "\n",
    "# Order the gates by cx count\n",
    "correct_kernels_cx_sorted = [\n",
    "    (correct_kernels[idx], num_cx[idx]) for idx in torch.argsort(num_cx)\n",
    "]\n",
    "\n",
    "# Draw a few of these circuits\n",
    "input_state = [0] * (2**num_of_qubits)\n",
    "input_state[0] = 1\n",
    "\n",
    "for correct_kernel, num_cx_gates in correct_kernels_cx_sorted[:3]:\n",
    "    kernel = correct_kernel.kernel\n",
    "    thetas = correct_kernel.params\n",
    "\n",
    "    print(f\"Generated circuit with {num_cx_gates} cx:\")\n",
    "    print(cudaq.draw(kernel, input_state, thetas))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd32819f-8bb6-4af8-bf69-84b600ba7ad5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CUDA-Q Version 0.11.0 (https://github.com/NVIDIA/cuda-quantum f5cc3bb1d85abcf1f642f4ddd20ad08bc1d4d200)\n",
      "genQC Version 0.2.3\n"
     ]
    }
   ],
   "source": [
    "print(cudaq.__version__)\n",
    "print(\"genQC Version\", genQC.__version__)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
